package com.example.exceldemo.service.impl;

import com.example.exceldemo.entity.DocFile;
import com.example.exceldemo.entity.DocFolder;
import com.example.exceldemo.exception.ServiceException;
import com.example.exceldemo.service.ZipImpService;
import com.github.junrar.Archive;
import com.github.junrar.exception.RarException;
import com.github.junrar.rarfile.FileHeader;
import lombok.extern.slf4j.Slf4j;

import org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry;
import org.apache.commons.compress.archivers.sevenz.SevenZFile;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;


/**
 * @Description
 * @Author xiaohuang
 * @Date 2025/2/24
 */
@Slf4j
@Service
public class ZipImpServiceImpl implements ZipImpService {

    // 定义上传路径为常量
    private static final String UPLOAD_Zip_PATH = "e://data//file//test//zip";
    private static final String UPLOAD_7Z_PATH = "e://data//file//test//7z";

    @Override
    public  List<DocFolder> zipImps(MultipartFile file) {
        List<DocFolder> folders = new ArrayList<>();
        List<DocFile> docFiles = new ArrayList<>();
        Long id = 1L;
        Integer num=1;
        Long fileId=1L;
        try (ZipInputStream zipInputStream = new ZipInputStream(file.getInputStream(), Charset.forName("GBK"))) {
            ZipEntry entry;
            while ((entry = zipInputStream.getNextEntry()) != null) {
                String entryName = entry.getName();
                // 忽略 MAC 系统的隐藏文件
                if (entryName.startsWith("__MACOSX/") || entryName.contains("/."))
                    continue;
                File outputFile = new File(UPLOAD_Zip_PATH, entryName);
                if (entry.isDirectory()) {
                    if (!outputFile.exists())
                    FileUtils.forceMkdir(outputFile);
                    //分割文件名，创建父子关系文件“/1/2”，分别创建父级1，子级2
                    String[] parts = entryName.split("/");
                    List<String> strings = Arrays.asList(parts);
                    initFold(strings, id, num, folders);
                } else {
                    // 处理文件,需要将文件进行上传操作
                    try (FileOutputStream fileOutputStream = new FileOutputStream(outputFile);
                         BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream, 1024)) {
                        byte[] bytes = new byte[1024];
                        int n;
                        while ((n = zipInputStream.read(bytes)) != -1) {
                            bufferedOutputStream.write(bytes, 0, n);
                        }
                    } catch (IOException e) {
                        // 记录日志并抛出异常
                        System.err.println("Error uploading file: " + e.getMessage());
                        throw new ServiceException("上传文件异常。。。");
                    }
                    addFile(entryName,docFiles,folders,fileId);
                }
            }
        } catch (IOException e) {
            throw new ServiceException("获取文件夹异常");
        }
        return folders;

    }



    private void initFold(DocFolder folder, DocFile docFile,List<DocFolder> folders){
        folders.forEach(f->{
            if (f.getId().equals(folder.getId())){
                f.getFiles().add(docFile);
            }
        });
    }

    @Override
    public List<DocFolder> rarImp(MultipartFile file) {
        String rarFilePath = "E://data//file//excel案例.rar"; // RAR 文件路径
        String outputDir = "e://data"; // 解压目标目录
        List<DocFolder> folders = new ArrayList<>();
        List<DocFile> docFiles = new ArrayList<>();
        Long id = 1L;
        Integer num=1;
        Long fileId=1L;
     //解压rar，获取所有文件
        // 使用 junrar 解压 RAR 文件
        File rarFile = new File(rarFilePath);
        File outputDirFile = new File(outputDir);

        // 确保目标目录存在
        if (outputDirFile.exists()) {
            //rar压缩算法是不开源,不支持解压winrar5以上
            try (Archive archive = new Archive(rarFile)) {
                FileHeader fileHeader;
                while ((fileHeader = archive.nextFileHeader()) != null) {
                    String fileName = fileHeader.getFileName(); // 获取文件名
                    File outputFile = new File(outputDir, fileName);

                    // 如果是文件夹，则创建目录
                    if (fileHeader.isDirectory()) {
                        FileUtils.forceMkdir(outputFile);
                    } else {
                        // 如果是文件，则写入文件内容
                        try (OutputStream os = new FileOutputStream(outputFile)) {
                            archive.extractFile(fileHeader, os);
                        }
                        System.out.println("解压文件: " + fileName);
                    }
                }
            } catch (RarException e) {
                throw new RuntimeException(e);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return null;

    }

    @Override
    public List<DocFolder> extract7z(MultipartFile file) {
        // 验证文件名并清理
        String originalFilename = sanitizeFilename(file.getOriginalFilename());
        if (originalFilename == null || originalFilename.isEmpty()) {
            throw new ServiceException("无效的文件名");
        }

        File tempFilePath = new File(UPLOAD_7Z_PATH, originalFilename);
        try {
            // 创建临时文件
            // 将文件内容写入临时文件
            try (InputStream inputStream = file.getInputStream()) {
                FileUtils.copyInputStreamToFile(inputStream, tempFilePath);
            }

            List<DocFolder> folders = new ArrayList<>();
            List<DocFile> docFiles = new ArrayList<>();
            Long id = 1L;
            Integer num = 1;
            Long fileId = 1L;

            try (SevenZFile sevenZ = new SevenZFile(tempFilePath)) {
                SevenZArchiveEntry entry;
                while ((entry = sevenZ.getNextEntry()) != null) {
                    // 获取文件名
                    String fileName = entry.getName();
                    File outputFile = new File(UPLOAD_7Z_PATH, fileName);
                    // 如果是文件夹，则创建目录
                    if (entry.isDirectory()) {
                        if (!outputFile.exists())
                            FileUtils.forceMkdir(outputFile);
                        // 分割文件名，创建父子关系文件“/1/2”，分别创建父级1，子级2
                        String[] parts = fileName.split("/");
                        List<String> strings = Arrays.asList(parts);
                        initFold(strings, id, num, folders);
                    } else {
                        // 如果是文件，则写入文件内容
                        try (OutputStream os = new FileOutputStream(outputFile)) {
                            byte[] buffer = new byte[8192];
                            int len;
                            while ((len = sevenZ.read(buffer)) > 0) {
                                os.write(buffer, 0, len);
                            }
                        }
                        addFile(fileName, docFiles, folders, fileId);
                        System.out.println("解压文件: " + fileName);
                    }
                }
            } catch (IOException e) {
                throw new ServiceException("解压文件失败", e);
            }

            return folders;
        } catch (IOException e) {
            throw new ServiceException("存储临时文件失败", e);
        } finally {
            // 删除临时文件
            if (tempFilePath != null) {
                try {
                   FileUtils.delete(tempFilePath);
                } catch (IOException e) {
                    throw new ServiceException("删除临时文件失败", e);
                }
            }
        }
    }

    private String sanitizeFilename(String filename) {
        if (filename == null) {
            return null;
        }
        // 清理文件名中的非法字符
        return filename.replaceAll("[^a-zA-Z0-9.-]", "_");
    }
    private void initFold( List<String> FileNames,Long id,Integer num,List<DocFolder> folders) {
        for (int i = 0; i < FileNames.size(); i++) {
            //父级
            if (i==0){
                DocFolder folder = new DocFolder();
                //先判断是否已创建父级
                String part = FileNames.get(0);
                Optional<DocFolder> first = folders.stream().filter(f -> f.getName().equals(part)).findFirst();
                if (!first.isPresent()){
                    folder.setId(id++);
                    folder.setFid("/"+folder.getId());
                    folder.setFname("/"+part);
                    folder.setLevel(1);
                    folder.setParentId(0L);
                    folder.setName(part);
                    folder.setOrderNum(num++);
                    folders.add(folder);
                }
            }else {
                //获取上一级文件夹
                String parentName = FileNames.get(i - 1);
                String name =  FileNames.get(i);
                Optional<DocFolder> first = folders.stream().filter(f -> f.getName().equals(parentName)).findFirst();
                //未创建该文件夹
                Optional<DocFolder> my = folders.stream().filter(f -> f.getName().equals(name)).findFirst();
                if (first.isPresent()&&!my.isPresent()) {
                    DocFolder folder = new DocFolder();
                    DocFolder parent = first.get();
                    folder.setId(id++);
                    folder.setFid(parent.getFid()+"/"+folder.getId());
                    folder.setFname(parent.getFname()+"/"+name);
                    folder.setParentId(parent.getId());
                    folder.setLevel(parent.getLevel()+1);
                    folder.setName(name);
                    //判断序号
                    List<DocFolder> list1 = folders.stream().filter(f -> f.getParentId().equals(parent.getId())).collect(Collectors.toList());
                    folder.setOrderNum(list1.size()+1);
                    folders.add(folder);
                }}}
    }

    private void addFile(String entryName,List<DocFile> docFiles ,List<DocFolder> folders, Long fileId) {
        String[] parts = entryName.split("/");
        // 将文件添加到当前文件夹中
        String fileName = parts[parts.length - 1];
        System.out.println(fileName);
        //判断它是哪个文件夹下面
        //获取最后一个/的位置
        int i = entryName.lastIndexOf("/");
        String parentFold = "/"+entryName.substring(0, i );
        //判断是否存在
        Optional<DocFolder> first = folders.stream().filter(f -> f.getFname().equals(parentFold)).findFirst();
        if (first.isPresent()) {
            String suffix = FilenameUtils.getExtension(fileName).toLowerCase();
            DocFolder folder = first.get();
            DocFile docFile = new DocFile();
            docFile.setId(fileId++);
            docFile.setFolderFid(folder.getFid());
            docFile.setFolderId(folder.getId());
            docFile.setFolderFname(folder.getFname());
            docFile.setFileName(fileName);
            docFile.setSuffix(suffix);
            docFiles.add(docFile);
            initFold(folder,docFile,folders);
        } else {
            log.warn("未找到用于条目的父文件夹: {}", entryName);
        }
    }

}
